转载自知乎网络,原文链接:https://zhuanlan.zhihu.com/p/47180589
loadService 函数
如何在 Egg 框架中使用 service
loadService 函数的实现是所有load函数中最复杂的一个,我们不着急看源码,先看一下 service 在 Egg 框架中如何使用
1 | // egg-core 源码 -> 如何在 egg 框架中使用 service |
我们上面列举了 service 下的 js 文件的四种写法,都是从每次请求的上下文 this.ctx 获取到 service 对象,然后就可以使用到每个 service 文件导出的对象了,这里主要有两个地方需要注意:
- 为什么我们可以从每个请求的 this.ctx 上获取到 service 对象呢:
看过 Koa 源码的同学知道,this.ctx 其实是从 app.context 继承而来,所以我们只要把 service 绑定到 app.context 上,那么当前请求的上下文 ctx 自然可以拿到 service 对象,EggLoader 也是这样做的 - 针对上述四种使用场景,具体导出实例是怎么处理的呢?
- 如果导出的是一个类,EggLoader 会主动以 ctx 对象去初始化这个实例并导出,所以我们就可以直接在该类中使用 this.ctx 获取当前请求的上下文了
- 如果导出的是一个函数,那么 EggLoader 会以 app 作为参数运行这个函数并将结果导出
- 如果是一个普通的对象,直接导出
FileLoader 类的实现分析
在实现 loadService 函数时,有一个基础类就是 FileLoader ,它同时也是 loadMiddleware,loadController 实现的基础,这个类提供一个 load 函数根据目录结构和文件内容进行解析,返回一个 target 对象,我们可以根据文件名以及子文件名以及函数名称获取到 service 里导出的内容,target 结构类似这样:
1 | { |
下面我们先看一下 FileLoader 这个类的实现:
1 | // egg-core 源码 -> FileLoader 实现 |
ContextLoader 类的实现分析
上文中说到 loadService 函数其实最终把 service 对象挂载在了 app.context 上,所以为此提供了 ContextLoader 这个类,继承了 FileLoader 类,用于将 FileLoader 解析出来的 target 挂载在 app.context 上,下面是其实现:
1 | // egg-core -> ContextLoader 类的源码实现 |
loadService 的实现
有了 ContextLoader 类,那实现 loadService 函数就非常容易了,如下:
1 | // egg-core -> loadService 函数实现源码 |
loadMiddleware 函数
中间件是 Koa 框架中很重要的一个环节,通过 app.use 引入中间件,使用洋葱圈模型,所以中间件加载的顺序很重要。 - 如果在上文中的 config 中配置的中间件,系统会自动用 app.use 函数使用该中间件 - 所有的中间件我们都可以在 app.middleware 中通过中间件 name 获取到,便于在业务中动态使用
1 | // egg-core 源码 -> loadMiddleware 函数实现源码 |
loadController 函数
controller 中生成的函数最终还是在 router.js 中当作一个中间件使用,所以我们需要将 controller 中内容转换为中间件形式 async function(ctx, next) ,其中 initializer 这个函数就是用来针对不同的情况将 controller 中的内容转换为中间件的,下面是 loadController 的实现逻辑:
1 | // egg-core源码 -> loadController 函数实现源码 |
loadRouter 函数
loadRouter 函数特别简单,只是 require 加载一下 app/router 目录下的文件而已,而所有的事情都交给了 EggCore 类上的 router 属性去实现
而 router 又是 Router 类的实例,Router 类是基于 koa-router 实现的
1 | // egg-core 源码 -> loadRouter 函数源码实现 |
Router 类继承了 KoaRouter 类,并对其的 method 相关函数做了扩展,解析 controller 的写法,同时提供了 resources 方法,为了兼容 restAPI 的请求方式
关于 restAPI 的使用方式和实现源码我们这里就不介绍了,可以看官方文档,有具体的格式要求,下面看一下 Router 类的部分实现逻辑:
1 | // egg-core源码 -> Router 类实现源码 |
总结
以上便是我对 egg-core 的大部分源码的实现的学习总结,其中关于源码中一些 debug 代码以及 timing 运行时间记录的代码都删掉了,关于 app 的生命周期管理的那部分代码和 loadUnits 加载逻辑关系不大,所以没有讲到。EggCore 的核心在于 EggLoader,也就是 plugin,config, extend, service, middleware, controller, router 的加载函数,而这几个内容加载必须按照顺序进行加载,存在依赖关系,比如:
- 加载 middleware 时会用到 config 关于应用中间件的配置
- 加载 router 时会用到关于 controller 的配置
- 而 config,extend,service,middleware,controller 的加载都必须依赖于 plugin,通过 plugin 配置获取插件目录
- service,middleware,controller,router 的加载又必须依赖于 extend(对 app 进行扩展),因为如果 exports 是函数的情况下,会将 app 作为参数执行函数
EggCore 是一个基础框架,其最重要的是需要遵循一定的约束和约定,可以保证一致的代码风格,而且提供了插件和框架机制,能使相同的业务逻辑实现复用,后面看有时间再写一下 Egg 框架的源码学习心得